package com.lzk.user.serice.impl;

import com.alibaba.fastjson.JSONObject;
import com.lzk.basic.constants.RegisterConstant;
import com.lzk.basic.excption.BusinessException;
import com.lzk.basic.jwt.JwtUtils;
import com.lzk.basic.jwt.RsaUtils;
import com.lzk.basic.util.HttpUtil;
import com.lzk.basic.util.JsonResult;
import com.lzk.basic.util.Md5Utils;
import com.lzk.basic.util.StrUtils;
import com.lzk.org.mapper.EmployeeMapper;
import com.lzk.system.domain.Menu;
import com.lzk.user.constant.WxConstants;
import com.lzk.user.domain.LoginData;
import com.lzk.user.domain.LoginInfo;
import com.lzk.user.domain.User;
import com.lzk.user.domain.WxUser;
import com.lzk.user.dto.BinderDto;
import com.lzk.user.dto.LoginDto;
import com.lzk.user.mapper.LoginInfoMapper;
import com.lzk.user.mapper.UserMapper;
import com.lzk.user.mapper.WxUserMapper;
import com.lzk.user.serice.ILoginService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;


@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LoginInfoMapper loginInfoMapper;
    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxUserMapper wxUserMapper;
    @Autowired
    private UserMapper userMapper;

    /**
     * 手机号登录
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> account(LoginDto dto) {
        /**
         * 1. 根据手机号或者邮箱和type查询loginInfo,
         * 2. 如果存在，则判断密码是否正确
         *   2.1： 加密的。  （你输入的密码+数据库的盐值）md5加密
         *         和数据库的密码对比
         * 3. 如果不存在，则直接提示，用户名不存在
         */
        LoginInfo loginInfo = loginInfoMapper.getLogininfoByDto(dto);
        if(loginInfo == null){
            //用户不存在
            throw new BusinessException("账户或密码错误");
        }
        //校验密码  加密加盐
        String md5Password = Md5Utils.encrypByMd5(dto.getPassword() + loginInfo.getSalt());
        //与数据库的加密过后密码对比
        if(!md5Password.equals(loginInfo.getPassword())){
            throw new BusinessException("密码错误，请重新输入！");
        }

       /* 账户与密码都无误后，存入redis，设置过期时间30分钟
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);

        //准备一个map
        HashMap<String, Object> map = new HashMap<>();

        map.put("token",token);
        //把密码和盐值处理
        loginInfo.setPassword(null);
        loginInfo.setSalt(null);
        map.put("logininfo",loginInfo);*/

        //登录成功后调用方法jwt token 响应给前端
        Map<String,Object> map = loginSuccessJwtHandler(loginInfo);

        return map;
    }

    /**
     * 微信登录
     * @param code
     * @return
     */
    @Override
    public JsonResult wechatLogin(String code) {
        //根据code发送请求获取token和openid
        String url = WxConstants.GET_ACK_URL
                .replace("APPID",WxConstants.APPID)
                .replace("SECRET",WxConstants.SECRET)
                .replace("CODE",code);
        //返回的json字符串
        String jsonStr = HttpUtil.httpGet(url);
        //将返回的json字符串转换为json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");

        //3.通过openid去t_wxuser中查询
        WxUser wxUser = wxUserMapper.loadByOpenid(openid);

        if(wxUser!=null && wxUser.getUser_id()!=null){//绑定过
            //4.有绑定过，直接免密登录
            //通过wxuser表的user_id(相当于user的id)去关联查询logininfo信息
            LoginInfo dbLoginInfo = loginInfoMapper.loadLogininfoByUserId(wxUser.getUser_id());
            String token = UUID.randomUUID().toString();
            //处理盐值和密码 --安全
            dbLoginInfo.setSalt(null);
            dbLoginInfo.setPassword(null);

          /*  redisTemplate.opsForValue().set(token,dbLoginInfo,30,TimeUnit.MINUTES);
            //准备map
            HashMap<String, Object> map = new HashMap<>();
            map.put("token",token);
            map.put("logininfo",dbLoginInfo);
            return JsonResult.me().setResultObj(map);*/

            //登录成功后调用方法jwt token 响应给前端
            Map<String,Object> map = loginSuccessJwtHandler(dbLoginInfo);
            return JsonResult.me().setResultObj(map);
        }else{
            //5.从没绑定过，响应给前端跳转绑定手机号页面
            String param = "?accessToken="+access_token+"&openId="+openid;
            return JsonResult.me(true,"绑定").setResultObj(param);
        }
    }

    /**
     * 微信绑定业务
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public JsonResult wechatBinder(BinderDto dto) {
        String phone = dto.getPhone();
        String verifyCode = dto.getVerifyCode();
        String accessToken = dto.getAccessToken();
        String openId = dto.getOpenId();
        //1.校验，非空校验，验证码校验---超时， 是否正确。
        if(StringUtils.isEmpty(phone)
            ||StringUtils.isEmpty(verifyCode)
            ||StringUtils.isEmpty(accessToken)
            ||StringUtils.isEmpty(openId)
        ){
            throw new BusinessException("参数不能为空");
        }

        //校验验证码
        Object obj = redisTemplate.opsForValue().get(phone + RegisterConstant.WXBINDER_CODE_PREFIX);
        if(obj==null){
            throw new BusinessException("验证码已过期，请重新输入！");
        }
        if(!verifyCode.equalsIgnoreCase(obj.toString().split(":")[0])){
            throw new BusinessException("验证码输入错误，请重新输入");
        }

        // 2. 向微信开放平台发送第三次请求。获取wxuser信息，将信息保存到wxuser, user, logininfo三张表
        String jsonObj = HttpUtil.httpGet(WxConstants.GET_USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId));
        //获取用户信息,通过jsonObj一个一个的set进wxuser
        WxUser wxUser = jsonObj2WxUser(jsonObj);

        //通过手机号查询user，能查到直接绑定
        User dbUser = userMapper.loadByPhone(phone);
        if(dbUser==null){//查不到
            //没有用手机号注册过，生成一个user对象，存值。
           dbUser =  phone2User(phone);
           //关联logininfo
            LoginInfo dbLoginInfo = user2Logininfo(dbUser);
            //保存logininfo，返回自增主键
            loginInfoMapper.save(dbLoginInfo);

            //保存user，放入logininfo_id
            dbUser.setLogininfo_id(dbLoginInfo.getId());
            //返回自增主键
            userMapper.save(dbUser);
        }
        //直接绑定wxUser
        wxUser.setUser_id(dbUser.getId());
        wxUserMapper.save(wxUser);

        //通过user中的logininfo_id查询logininfo
        Long logininfo_id = dbUser.getLogininfo_id();
        LoginInfo loginInfo = loginInfoMapper.loadById(logininfo_id);
        // 把盐值，和密码设置为null
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
       /* //免密登录
        String token = UUID.randomUUID().toString();
        // 设置三十分钟过去
        redisTemplate.opsForValue().set(token,loginInfo,30,TimeUnit.MINUTES);
        // 准备一个map
        HashMap<String, Object> map = new HashMap<>();
        map.put("logininfo",loginInfo);
        map.put("token",token);*/

        //登录成功后调用方法jwt token 响应给前端
        Map<String,Object> map = loginSuccessJwtHandler(loginInfo);
        return JsonResult.me().setResultObj(map);
    }

    /**
     * 获取jwt token
     * 1.获取logininfo
     * 2.获取当前登陆人的所有权限 - sn
     * 3.获取当前登陆人的所有菜单 - 【难度】
     * 4.通过私钥对登录信息进行加密 - jwtToken串
     * 5.转到map返回
     * @param loginInfo
     * @return
     */
    private Map<String, Object> loginSuccessJwtHandler(LoginInfo loginInfo){

        try {
            //创建loginData通过私钥加密
            LoginData loginData = new LoginData();
            loginData.setLoginInfo(loginInfo);
            Map<String,Object> map = new HashMap<>();
            //获取logininfo
            if(loginInfo.getType() == 0){//管理员
                //获取当前登陆人的所有权限 sn
                List<String> permissions = employeeMapper.loadPermissionsByLogininfoId(loginInfo.getId());
                //获取当前登陆人的所有菜单
                List<Menu> menus = employeeMapper.loadMenusByLogininfoId(loginInfo.getId());
                loginData.setPermissions(permissions);
                loginData.setMenus(menus);
                //将当前登陆人的权限和菜单添加到map - 响应给前端
                map.put("permissions",permissions);
                map.put("menus",menus);
            }
            //通过私钥对登录信息进行加密 - jwtToken串
            PrivateKey privateKey = RsaUtils.getPrivateKey(RsaUtils.class.getClassLoader()
                    .getResource("auth_rsa.pri").getFile());
            //将登陆人信息加密得到jwtToken串
            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 3000);
            //装到map返回
            map.put("token",jwtToken);
            map.put("logininfo",loginInfo);
            return map;
        } catch (Exception e) {
           e.printStackTrace();
        }

        return null;
    }

    /**
     * 通过手机号保存，生成userd对象
     * @param phone
     * @return
     */
    private User phone2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        //生成盐值
        String salt = StrUtils.getComplexRandomString(32);
        user.setSalt(salt);

        //生成一个密码
        String password = StrUtils.getComplexRandomString(6);
        //加密加盐
        String md5Password = Md5Utils.encrypByMd5(password + salt);
        user.setPassword(md5Password);
        return user;
    }

    /**
     * 通过user保存logininfo
     * @param dbUser
     * @return
     */
    private LoginInfo user2Logininfo(User dbUser) {
        LoginInfo loginInfo = new LoginInfo();
        //1为普通用户
        loginInfo.setType(1);
        BeanUtils.copyProperties(dbUser,loginInfo);
        return loginInfo;
    }

    /**
     *返回的微信用户信息,set到wxuser表
     * @param jsonObj
     * @return
     */
    private WxUser jsonObj2WxUser(String jsonObj) {
        //将jsonObj转换为json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonObj);
        WxUser wxUser = new WxUser();
        wxUser.setAddress(jsonObject.getString("country"));
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        return wxUser;
    }
}
